<template>
  <div ref="menuContainer" class="menu-container">
    <slot></slot>
    <Teleport to="body">
      <Transition name="fade">
        <div
          class="menu"
          v-if="showMenu"
          :style="{ left: pos.x + 'px', top: pos.y + 'px' }"
        >
          <ul v-size-ob="handleSizeChange">
            <li
              v-for="item in menu"
              :class="{ disabled: item.disabled, divided: item.divided }"
              @click="selectMenu(item)"
            >
              <slot name="custom">
                <span class="button">
                  {{ item.label }}
                </span>
              </slot>
            </li>
          </ul>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<script setup>
import { useViewport } from "@/hooks";
import { onMounted, onBeforeUnmount, useTemplateRef, ref, computed } from "vue";
const props = defineProps({
  offset: {
    type: [Array, Number],
    default: 0,
  },
  menu: { type: Array, default: () => [] },
});
const emits = defineEmits(["select", "close", "open"]);
const menuContainer = useTemplateRef("menuContainer");
// 格式化offset
const offset = computed(() => {
  return Array.isArray(props.offset)
    ? props.offset
    : [props.offset, props.offset];
});
// 视口尺寸
const { vh, vw } = useViewport();
const showMenu = ref(false);
// 菜单尺寸
const menuSize = ref({ width: 0, height: 0 });
const x = ref(0);
const y = ref(0);
const pos = computed(() => {
  const [offsetX, offsetY] = offset.value;
  let posX = x.value + offsetX;
  let posY = y.value + offsetY;
  if (x.value + offsetX > vw.value - menuSize.value.width) {
    posX = x.value - menuSize.value.width - offsetX;
  }
  if (y.value + offsetY > vh.value - menuSize.value.height) {
    posY = vh.value - menuSize.value.height - offsetY - 20;
  }
  return { x: posX, y: posY };
});

onMounted(() => {
  menuContainer.value.addEventListener("contextmenu", handleContextMenu);
  window.addEventListener("click", closeMenu, true);
  window.addEventListener("contextmenu", closeMenu, true);
});
onBeforeUnmount(() => {
  menuContainer.value.removeEventListener("contextmenu", handleContextMenu);
  window.removeEventListener("click", closeMenu, true);
  window.removeEventListener("contextmenu", closeMenu, true);
});
// 右键
function handleContextMenu(e) {
  e.preventDefault();
  e.stopPropagation();
  showMenu.value = true;
  x.value = e.clientX;
  y.value = e.clientY;
  emits("open");
}
// 关闭菜单
function closeMenu() {
  showMenu.value = false;
  emits("close");
}
// 选择菜单
function selectMenu(item) {
  if ("disabled" in item && item.disabled) {
    return;
  }
  if (item.onClick) {
    item.onClick();
  }
  emits("select", item);
}
// 菜单尺寸
function handleSizeChange(size) {
  menuSize.value.width = size.width;
  menuSize.value.height = size.height;
}
</script>

<style scoped>
.menu {
  position: absolute;
  padding: 10px 0;
  background: var(--bg-primary);
  border-radius: 5px;
  box-shadow: 0 0 10px var(--shadow-color);
  width: 15rem;
}
.menu li {
  width: 100%;
  border-radius: 5px;
  transition: background 0.3s;
  display: flex;
  font-size: 0.9rem;
  color: var(--text-primary);
}
.menu li:hover {
  background: var(--bg-hover);
}
.menu li:hover .button {
  color: var(--text-hover);
}
.menu li.divided {
  border-top: 1px solid var(--border-color);
}
.menu li.disabled:hover {
  background: none;
}

.menu li.disabled .button {
  color: var(--disabled-color);
}

.menu li .button {
  width: 100%;
  padding: 5px 12px;
  height: 100%;
  border: none;
  background: none;
  transition: color 0.3s;
  cursor: pointer;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}
</style>
